 <template>
    <div class="file-upload" >
    <div class="btn btn-primary file-upload-contanier" @click.prevent="triggerUpload" v-bind='$attrs'>
      <slot name="loading" v-if="status=== 'loading'"><span >正在上传</span></slot>
      <slot name='successload' v-else-if="status=== 'success'" :uploadedData='uploadedData'><span >上传成功</span></slot>
      <slot name='default' v-else><span>单击上传</span></slot>
    </div>
    <input type='file' class="file-input d-none " ref="fileInput" @change="handleChange">
    </div>
</template>

<script lang='ts'>
import { defineComponent, ref, PropType, watch } from 'vue'
import axios from 'axios'
type UploadStatus = 'ready' | 'loading' | 'success' | 'error'
type CheckFunction= (file: File) => boolean // 检测这个数据的格式。
export default defineComponent({
  name: 'Uploader',
  props: {
    action: { // 目标地址
      type: String,
      required: true
    },
    uploaded: {
      type: Object
    },
    beforeUpload: {
      type: Function as PropType<CheckFunction>,
      required: false
    }// 这里是在说这个在用户上传文件之前，需要进行一定得检查
  },
  inheritAttrs: false, // 禁止来继承属性。就是父组件向子组件传递了一些信息，但我不想让这里的子容器出现这个继承现象
  emits: ['file-uploaded', 'file-uploaded-error'], // 就是向外发送一个成功的处理，函数，和一个失败的处理函数
  setup (props, context) {
    const fileInput = ref<null | HTMLInputElement>(null)
    console.log(props.uploaded)

    const status = ref<UploadStatus>(props.uploaded ? 'success' : 'ready')// 这里的状态具体是什么意思
    const uploadedData = ref(props.uploaded)// 数据的一个初始化
    console.log(uploadedData.value)

    watch(() => props.uploaded, (newdata) => {
      if (newdata) {
        console.log(newdata)

        status.value = 'success'
        uploadedData.value = newdata
      }// 这里就是说监听到这个uploaded状态的改变的时候，我就可以改变这个插槽里定义的内容，从而在父组件里去拿到这个新的值，比如新的头图。
    })
    const triggerUpload = () => {
      if (fileInput.value) {
        fileInput.value.click()
      }
    }
    const handleChange = (e: Event) => {
      const currentTarget = e.target as HTMLInputElement
      if (currentTarget.files) {
        const files = Array.from(currentTarget.files)
        if (props.beforeUpload) {
          const result = props.beforeUpload(files[0])
          if (!result) {
            return // 就是说如果不满足用户定义得规则，就禁止上传文件二零
          }
        }

        status.value = 'loading'
        const formdata = new FormData()
        // 我需要把这个你所传递的文件转化为数组，不过这里为什么一定要用数组，为了之后能够去获取这个首部元素。
        formdata.append('file', files[0])// 就是有多个文件，但是这里我只传一个
        axios.post(props.action, formdata, {
          headers: {
            'Content-Type': 'multipart/form-data' // 就是向服务器发送数据的时候，是以这个格式来发送的，。
          }
        }).then((resp) => {
          status.value = 'success'
          console.log(resp.data)

          uploadedData.value = resp.data
          context.emit('file-uploaded', resp.data)
        }).catch((error) => {
          status.value = 'error'
          context.emit('file-uploaded-error', { error }) // 这里为何一定要用这个，对象
        }).finally(() => {
          if (fileInput.value) {
            fileInput.value.value = ''// 这个意思就是说，你每次如果上传文件不去赋空这个值得话，那你下次不还是会传递同样得文件吗对吧
          }
          // status.value = 'ready' //因为你上传成功后我要展示你成功上传的图片所以，不能再初始化这个数据了
        })
      }
    }
    return {
      triggerUpload,
      status,
      handleChange,
      fileInput, // 凡是在组件中用到的值，必须导出，否则组件拿不到这个值，思路必须有
      uploadedData
    }
  }
}
)
</script>

<style>
.file-upload .file-upload-contanier {
  height: 200px;
  cursor: pointer;
}
.file-upload .file-upload-contanier img{
  height: 100%;
  width: 100%;
  object-fit: cover;
  /* 就是用来专门处理这个图片的 */
}
</style>
